@file:OptIn(ExperimentalForeignApi::class)

package com.tencent.compose.sample

import androidx.compose.foundation.layout.Column
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.napi.JsEnv
import androidx.compose.ui.napi.asString
import com.tencent.compose.utils.FooObject
import com.tencent.compose.utils.JsFoo
import com.tencent.compose.utils.TsEnv
import com.tencent.compose.utils.TestString
import com.tencent.compose.utils.TsFoo
import com.tencent.compose.utils.createString
import kotlinx.cinterop.ExperimentalForeignApi
import kotlinx.cinterop.cstr
import kotlinx.cinterop.interpretCPointer
import kotlinx.cinterop.rawValue
import platform.test725.print_time
import kotlin.time.Clock
import kotlin.time.ExperimentalTime
import kotlin.time.measureTime
import platform.ohos.napi_value
import kotlin.time.Duration

@Composable
internal fun KotlinCallArkTS() {
    Column() {
        Text("Kotlin调用ArkTS的场景")
        Button(onClick = { callArkTSFun1() }) { Text("callArkTSFun1") }
        Button(onClick = { callArkTSFun1AfterOpt() }) { Text("callArkTSFun1AfterOpt") }
        Button(onClick = { timeTest() }) { Text("timeTest") }
        Button(onClick = { objTest() }) { Text("objTest") }
        Button(onClick = { fooTest() }) { Text("fooTest") }
    }
}

@OptIn(ExperimentalTime::class)
fun callArkTSFun1() {
    print_time("kn: callArkTSFun1 tag1".cstr)
    var current = Clock.System.now()
    val global = JsEnv.getGlobal()
    // calcTime("callArkTSFun1 tag1", current)

    print_time("kn: callArkTSFun1 tag2".cstr)
    current = Clock.System.now()
    val funcName = JsEnv.createStringUtf8("arkTsFun1")
    // calcTime("callArkTSFun1 tag2", current)

    current = Clock.System.now()
    val func1 = JsEnv.getProperty(global, funcName)
    // calcTime("callArkTSFun1 tag3", current)

    print_time("kn: callArkTSFun1 tag3".cstr)
    current = Clock.System.now()
    val param = JsEnv.createStringUtf8("param1")
    // calcTime("callArkTSFun1 tag4", current)
    current = Clock.System.now()
    val result = JsEnv.callFunction(global, func1, param)
    // calcTime("callArkTSFun1 tag5", current)

    print_time("kn: callArkTSFun1 tag4".cstr)
    println("kn: result=${result.asString()}")
}

@OptIn(ExperimentalTime::class)
fun callArkTSFun1AfterOpt() {
    print_time("kn: callArkTSFun1AfterOpt tag1".cstr)
    var current = Clock.System.now()
    val global = TsEnv.getGlobal()
    // calcTime("callArkTSFun1AfterOpt tag1", current)

    print_time("kn: callArkTSFun1AfterOpt tag2".cstr)
    current = Clock.System.now()
    val funcName = TsEnv.createStringUtf8("arkTsFun1")
    // calcTime("callArkTSFun1AfterOpt tag2", current)
    current = Clock.System.now()
    val func1 = TsEnv.getProperty(global, funcName)
    // calcTime("callArkTSFun1AfterOpt tag3", current)
    print_time("kn: callArkTSFun1AfterOpt tag3".cstr)
    current = Clock.System.now()
    val param = TsEnv.createStringUtf8("param1")
    // calcTime("callArkTSFun1AfterOpt tag4", current)
    current = Clock.System.now()
    val result = TsEnv.callFunction(global, func1, param)
    // calcTime("callArkTSFun1AfterOpt tag5", current)
    print_time("kn: callArkTSFun1AfterOpt tag4".cstr)
    println("kn: result=${result?.asString()}")
}

fun timeTest() {
    val loopCount = 10
    var duration: Duration = Duration.ZERO
    val env = JsEnv.env().rawValue

    // String
    duration = measureTime { repeat(loopCount) { _ -> JsEnv.createStringUtf8(TestString) } }
    println("kn: String JsEnv duration=${duration.inWholeNanoseconds} ns")
    duration = measureTime { repeat(loopCount) { _ -> TsEnv.createStringUtf8(TestString) } }
    println("kn: String TsEnv duration=${duration.inWholeNanoseconds} ns")
    duration = measureTime { repeat(loopCount) { _ -> createString(TestString) } }
    println("kn: String afterOpt duration=${duration.inWholeNanoseconds} ns")

    // Int
    val intValue = 10000
    duration = measureTime { repeat(loopCount) { _ -> JsEnv.createInt32(intValue) } }
    println("kn: Int JsEnv duration=${duration.inWholeNanoseconds} ns")
    duration = measureTime { repeat(loopCount) { _ -> TsEnv.createInt32(intValue) } }
    println("kn: Int TsEnv duration=${duration.inWholeNanoseconds} ns")
    duration = measureTime { repeat(loopCount) { _ -> napi_create_kotlin_int32(env, intValue) } }
    println("kn: Int afterOpt duration=${duration.inWholeNanoseconds} ns")

    // Long
    val longValue = 10000L
    duration = measureTime { repeat(loopCount) { _ -> JsEnv.createInt64(longValue) } }
    println("kn: Long JsEnv duration=${duration.inWholeNanoseconds} ns")
    duration = measureTime { repeat(loopCount) { _ -> TsEnv.createInt64(longValue) } }
    println("kn: Long TsEnv duration=${duration.inWholeNanoseconds} ns")
    duration =
        measureTime { repeat(loopCount) { _ -> napi_create_kotlin_int64(env, longValue) } }
    println("kn: Long afterOpt duration=${duration.inWholeNanoseconds} ns")

    // Double
    val doubleValue = 10000.0
    duration = measureTime { repeat(loopCount) { _ -> JsEnv.createDouble(doubleValue) } }
    println("kn: Double JsEnv duration=${duration.inWholeNanoseconds} ns")
    duration = measureTime { repeat(loopCount) { _ -> TsEnv.createDouble(doubleValue) } }
    println("kn: Double TsEnv duration=${duration.inWholeNanoseconds} ns")
    duration =
        measureTime { repeat(loopCount) { _ -> napi_create_kotlin_double(env, doubleValue) } }
    println("kn: Double afterOpt duration=${duration.inWholeNanoseconds} ns")

    // Boolean
    val booleanValue = false
    duration = measureTime { repeat(loopCount) { _ -> JsEnv.getBoolean(booleanValue) } }
    println("kn: Boolean JsEnv duration=${duration.inWholeNanoseconds} ns")
    duration = measureTime { repeat(loopCount) { _ -> TsEnv.getBoolean(booleanValue) } }
    println("kn: Boolean TsEnv duration=${duration.inWholeNanoseconds} ns")
    duration =
        measureTime { repeat(loopCount) { _ -> napi_create_kotlin_bool(env, booleanValue) } }
    println("kn: Boolean afterOpt duration=${duration.inWholeNanoseconds} ns")
}

fun objTest() {
    val loopCount = 10000
    var duration: Duration = Duration.ZERO
    // Object
    val env = JsEnv.env()
    val obj = JsEnv.createObject()
    val objKey = JsEnv.createStringUtf8("key")
    val objValue = JsEnv.createStringUtf8(TestString)
    JsEnv.setProperty(obj, objKey, objValue)

    duration = measureTime { repeat(loopCount) { _ -> JsEnv.getProperty(obj, objKey) } }
    println("kn: Object JsEnv duration=${duration.inWholeNanoseconds} ns")
    duration = measureTime { repeat(loopCount) { _ -> TsEnv.getProperty(obj, objKey) } }
    println("kn: Object TsEnv duration=${duration.inWholeNanoseconds} ns")
//    duration = measureTime {
//        repeat(loopCount) { _ ->
//            val res: napi_value? = interpretCPointer(
//                Knapi_get_property(
//                    env.rawValue,
//                    objKey.rawValue,
//                    objValue.rawValue
//                )
//            )
//        }
//    }
//    println("kn: Object afterOpt duration=${duration.inWholeNanoseconds} ns")
}

fun fooTest() {
    val loopCount = 100
    var duration: Duration = Duration.ZERO

    val foo = FooObject(
        id = "rootId",
        boolValue = true,
        intValue = 1000,
        longValue = 10001000L,
        doubleValue = 101.2123,
        fooValue = FooObject(id = "id2", fooValue = FooObject(id = "id3", longValue = 20001000)),
        listValue = List<FooObject?>(5) { index -> FooObject(id = "foo-list-item-$index") },
        mapValue = arrayOf(
            "key1",
            "key2",
            "key3"
        ).associateWith { FooObject(id = "foo-map-item-$it") },
        mapListValue = arrayOf("ml1", "ml2", "ml3").associateWith {
            arrayOf("mlItem1", "mlItem2", "mlItem3").map { FooObject(id = "foo-map-list-item-$it") }
        }
    )
    val global = JsEnv.getGlobal()
    val funcName = JsEnv.createStringUtf8("fooArkTsFunc1")
    val func1 = JsEnv.getProperty(global, funcName)
    var res: napi_value? = null
    duration = measureTime {
        repeat(loopCount) { _ ->
            // 优化前
            print_time("kn: kkk tag1".cstr)
            val value = JsFoo.toNapiValue(foo) // FooObject 转 napi_value
            print_time("kn: kkk tag2".cstr)
            res = JsEnv.callFunction(global, func1, value) // 调用 ArkTS 方法
            print_time("kn: kkk tag5".cstr)
            val resObj : FooObject? = JsFoo.toFooObject(res) // napi_value 转 FooObject
            print_time("kn: kkk tag6".cstr)
        }
    }
    val resFoo = JsFoo.toFooObject(res)
    println("kn: fooTest JsEnv duration=${duration.inWholeNanoseconds / loopCount} ns, intValue=${resFoo?.intValue}, str9Value=${resFoo?.str9Value}")

    duration = measureTime {
        repeat(loopCount) { _ ->
            // 优化后的实现，待替换
            val value = TsFoo.toNapiValue(foo)
            TsEnv.callFunction(global, func1, value)
        }
    }
    println("kn: fooTest TsEnv duration=${duration.inWholeNanoseconds / loopCount} ns")
}